package cn.org.donald.framework.authentication.controller;
import cn.org.donald.framework.service.SysLoginService;
import cn.org.donald.framework.service.SysPermissionService;
import cn.org.donald.framework.service.SysRoleService;
import cn.org.donald.framework.service.SysUserService;
import cn.org.donald.framework.constant.EncodeConstant;
import cn.org.donald.framework.constant.InfoConstant;
import cn.org.donald.framework.constant.RegisterConstant;
import cn.org.donald.framework.constant.TableConstant;
import cn.org.donald.framework.controller.BaseController;
import cn.org.donald.framework.handler.ServiceException;
import cn.org.donald.framework.pojo.vo.HttpResult;
import cn.org.donald.framework.pojo.dto.LoginBody;
import cn.org.donald.framework.pojo.dto.LoginUser;
import cn.org.donald.framework.property.CaptchaProperties;
import cn.org.donald.framework.util.JwtUtil;
import cn.org.donald.framework.util.RequestUtil;
import cn.org.donald.pojo.entity.Role;
import cn.org.donald.pojo.entity.RolePermission;
import cn.org.donald.pojo.entity.User;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author ： Donald
 * @date ： 2020/10/16 21:43
 * @description：
 */
@SuppressWarnings("unchecked")
@RestController
@RequestMapping("/system")
@Api(value = "系统功能", description = "登录，注销，验证码等功能")
public class SystemController extends BaseController {

    @Autowired
    private CaptchaProperties captchaProperties;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysPermissionService sysPermissionService;

    @Autowired
    private SysLoginService sysLoginService;

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/preLogin")
    @ApiOperation(value = "登录拦截提示接口")
    public HttpResult preLogin(){
        throw new UnauthenticatedException();
    }

    /**
     * 获取验证码
     * @return
     */
    @GetMapping("/getCaptcha")
    @ApiOperation(value = "获取验证码接口")
    public HttpResult getCaptcha() throws IOException {
        if (captchaProperties.getEnable()){

            String captcha = defaultKaptcha.createText();
            // 创建验证码图片
            BufferedImage image = defaultKaptcha.createImage(captcha);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "jpg", outputStream);
            // 生成验证码token
            String captchaToken = UUID.randomUUID().toString();

            // 将验证码存入redis(captchaToken-random)
            redisTemplate.opsForValue().set(captchaToken,captcha+"",captchaProperties.getValidity(), TimeUnit.SECONDS);
            Map<String,Object> captchaMap = new HashMap<>();
            captchaMap.put("img", Base64.getEncoder().encodeToString(outputStream.toByteArray()));

            captchaMap.put("captchaToken", captchaToken);
            IOUtils.closeQuietly(outputStream);
            return toResultSuccess(captchaMap);
        }
        return toResultSuccess();
    }

    @PostMapping("/login")
    @ApiOperation(value = "登录获取身份token接口")
    @ApiImplicitParam(name = "LoginBody",value = "登录参数",dataType = "LoginBody")
    public HttpResult toLogin(@RequestBody LoginBody loginBody,HttpServletRequest request){

        if (captchaProperties.getEnable()){
            //从redis获取captchaToken并验证
            Object captchaObj = redisTemplate.opsForValue().get(loginBody.getCaptchaToken());
            if (captchaObj==null){
                throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR.value(),InfoConstant.CAPTCHA_EXPIRED);
            }
            String captcha = captchaObj.toString();
            if (!captcha.equalsIgnoreCase(loginBody.getCaptcha())){
                throw new ServiceException(HttpStatus.UNAUTHORIZED.value(),InfoConstant.CAPTCHA_ERROR);
            }
        }
        try {
           //登录成功后需要将JWT传给前端
            User user = sysUserService.findByUserName(loginBody.getUsername());
            if (user==null){
                logger.error("登录错误:{}", InfoConstant.USER_NOT_EXIST);
                throw new UnknownAccountException(InfoConstant.USER_NOT_EXIST);
            }
            /*
            参数1 密码
            参数2 加盐值
            参数3 散列次数
             */
            Md5Hash md5Hash = new Md5Hash(loginBody.getPassword(), user.getSaltValue(), EncodeConstant.HASH_ITERATIONS);
            if (user.getPassword().equals(md5Hash.toHex())){
                //登录成功
                LoginUser loginUser = new LoginUser();
                //查询用户角色和权限
                List<Role> roleList = sysRoleService.findByUserId(user.getId());
                List<RolePermission> rolePermissionList = sysPermissionService.findRolePermissionByRoleIds(roleList);
                //放入loginUser
                loginUser.setUserId(user.getId());
                loginUser.setUserName(user.getUserName());
                loginUser.setRoleList(roleList);
                loginUser.setRolePermissionList(rolePermissionList);

                //保存登录信息
                if(!sysLoginService.saveLoginInfo(loginUser,request)){
                    toResultFail("登录异常");
                }

                //设置token
                Map<String,Object> tokenMap = JwtUtil.getLoginToken(loginUser);
                String token = (String) tokenMap.get(JwtUtil.TOKEN_NAME);
                Long expireTime = (Long) tokenMap.get(JwtUtil.EXPIRE_TIME_NAME);
                //将登录对象存入redis
                redisTemplate.opsForValue().set(token,loginUser,expireTime,TimeUnit.MILLISECONDS);
                HashMap<String, Object> map = new HashMap<>();
                map.put(EncodeConstant.AUTHORIZATION_NAME,token);
                map.put(TableConstant.FIELD_USER_NAME,user.getUserName());
                map.put(TableConstant.FIELD_USER_ID,user.getId());


                return toResultSuccess(InfoConstant.LOGIN_SUCCESS,map);
            }else {
                //登录失败
                logger.error("登录错误:{}", InfoConstant.PASSWORD_INCORRECT);
                throw new IncorrectCredentialsException(InfoConstant.PASSWORD_INCORRECT);
            }

        }catch (UnknownAccountException e){
            logger.error("登录错误:{}", InfoConstant.USER_NOT_EXIST);
            throw new UnknownAccountException(InfoConstant.USER_NOT_EXIST);
        }catch (IncorrectCredentialsException e){
            throw new IncorrectCredentialsException(InfoConstant.PASSWORD_INCORRECT);
        }
    }

    @GetMapping("/logout")
    @ApiOperation(value = "用户登出接口")
    public HttpResult logout(HttpServletRequest request){
        logger.info("注销登录");
        String authorization = RequestUtil.getAuthorization(request);
        redisTemplate.delete(authorization);
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return toResultSuccess(InfoConstant.LOGOUT_SUCCESS);
    }


    @PostMapping("/register")
    @ApiOperation(value = "用户注册接口")
    @ApiImplicitParam(name = "User", value = "用户实体参数",dataType = "User")
    public HttpResult register(@RequestBody User user){
        try {
            if (user.getUserName()!=null&&sysUserService.isExistByUserName(user.getUserName())){
                return new HttpResult(HttpStatus.INTERNAL_SERVER_ERROR.value(), RegisterConstant.USER_NAME_EXIST);
            }else if (user.getPhone()!=null&&sysUserService.isExistByPhone(user.getPhone())){
                return new HttpResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),RegisterConstant.PHONE_EXIST);
            }else if (user.getEmail()!=null&&sysUserService.isExistByEmail(user.getEmail())){
                return new HttpResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),RegisterConstant.EMAIL_EXIST);
            }
            sysUserService.insert(user);
        }catch (Exception e){
            e.printStackTrace();
            return new HttpResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),RegisterConstant.FAIL);
        }
        return new HttpResult(HttpStatus.OK.value(),RegisterConstant.SUCCESS);
    }

}
